package com.ruoyi.leave.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.core.redis.RedisCache;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class WorkFlowCache {
    @Autowired
    private RedisCache redisCache;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;

    static final String CACHE_DEFINITION_ID = "CACHE_DEFINITION_ID";
    static final String CACHE_FLOW_ELEMENT_LIST = "CACHE_FLOW_ELEMENT_LIST";
    static final String CACHE_USER_TASK_LIST = "CACHE_FLOW_ELEMENT_LIST";

    static Map<String, Collection<FlowElement>> flowElementMap = new HashMap<>();

    static Map<String, Map<String, UserTask>> userTaskMap = new HashMap<>();

    public String getWaterPlanProcessDefinitionId(String processInstanceId, String processDefinitionKey) {
        Object cacheObject = redisCache.getCacheObject(CACHE_DEFINITION_ID.concat(processInstanceId));
        if (Objects.nonNull(cacheObject)) {
            return StrUtil.toStringOrNull(cacheObject);
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .orderByProcessDefinitionVersion().desc()
                .singleResult();
        redisCache.setCacheObject(CACHE_DEFINITION_ID.concat(processInstanceId), processDefinition.getId(), 1, TimeUnit.DAYS);
        return processDefinition.getId();
    }

    public Collection<FlowElement> getWaterPlanFlowElementList(String processDefinitionId) {
        Object cacheObject = redisCache.getCacheObject(CACHE_FLOW_ELEMENT_LIST.concat(processDefinitionId));
        if (Objects.nonNull(cacheObject) && CollectionUtil.isNotEmpty(flowElementMap) && flowElementMap.containsKey(processDefinitionId)) {
            return flowElementMap.get(processDefinitionId);
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        List<Process> processList = bpmnModel.getProcesses();
        if (CollectionUtil.isNotEmpty(processList)) {
            Collection<FlowElement> flowElements = processList.get(0).getFlowElements();
            redisCache.setCacheObject(CACHE_FLOW_ELEMENT_LIST.concat(processDefinitionId), processDefinitionId, 1, TimeUnit.DAYS);
            flowElementMap.put(processDefinitionId, flowElements);
            return flowElements;
        }
        return null;
    }

    public Map<String, UserTask> getUserTaskList(String processDefinitionId) {
        Object cacheObject = redisCache.getCacheObject(CACHE_USER_TASK_LIST.concat(processDefinitionId));
        if (Objects.nonNull(cacheObject) && CollectionUtil.isNotEmpty(userTaskMap) && userTaskMap.containsKey(processDefinitionId)) {
            return userTaskMap.get(processDefinitionId);
        }
        Map<String, UserTask> resMap = new TreeMap<>();
        Collection<FlowElement> waterPlanFlowElementList = getWaterPlanFlowElementList(processDefinitionId);
        Iterator<FlowElement> iterator = waterPlanFlowElementList.iterator();
        while (iterator.hasNext()) {
            FlowElement flowElement = iterator.next();
            if (flowElement instanceof UserTask) {
                UserTask userTask = (UserTask) flowElement;
                resMap.put(userTask.getId(), userTask);
            }
        }
        redisCache.setCacheObject(CACHE_USER_TASK_LIST.concat(processDefinitionId), processDefinitionId, 1, TimeUnit.DAYS);
        userTaskMap.put(processDefinitionId, resMap);
        return resMap;
    }
}
